/************************************************************************
              ConversionManager.cpp - Copyright Claudio Botta

This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110, USA

**************************************************************************/


#include "ConversionManager.h"

#include <boost/config.hpp>

#include <fstream>

#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>

using namespace boost;
using namespace geomagick;

#if 1
// Constructors/Destructors
//

//
// Methods
//


// Accessor methods
//


// Public static attribute accessor methods
//


// Public attribute accessor methods
//


// Protected static attribute accessor methods
//


// Protected attribute accessor methods
//


// Private static attribute accessor methods
//
/** Retrieve the VertexNumber which is associated with the BaseType named name
* @param name the name of the Basetype to find
* @return the Number of the Vertex on success, -1 on failure
*/
int ConversionManager::getVertexNumberbyName(string name)
{
	 int VertexNumber = 0;
	 foreach(BaseTypeId* registeredBaseType, BaseTypes) {
                if (registeredBaseType->getTypeName().compare(name) == 0) 	
		{
			return VertexNumber;
		}
		VertexNumber++;
	}
	return -1;
}
// Private  methods
void ConversionManager::rebuildGraph()
{
	this->CGraph->clear();
	this->CGraph->addVertices(this->BaseTypes.size());
	foreach(ConversionId * registeredConversion, Conversions)
	{
		int from = this->getVertexNumberbyName(registeredConversion->getSourceId()->getTypeName());
		int to	 = this->getVertexNumberbyName(registeredConversion->getTargetId()->getTypeName());
		if(from != -1 && to != -1)
		{
			this->CGraph->addEdge(E(from,to),registeredConversion->getEdgeWeight());

		}
		
	}
}


// Other methods
//
/**
* Register a new BaseType
* @param Id the Id of the BaseType to register
* @return 0 on success, -1 on failure
*/
int ConversionManager::registerBaseType(BaseTypeId* Id)
{
#if DEBUG
        cout << "Test: " << Id->getTypeName() << endl;
#endif
        bool alreadyInList = false;
        foreach(BaseTypeId* registeredBaseType, BaseTypes) {
                if (registeredBaseType->getTypeName().compare(Id->getTypeName()) == 0) {
#if DEBUG
                        cout << Id->getTypeName() << " has already been registered!\n";
#endif
                        alreadyInList = true;
                }
        }
        if (!alreadyInList) {
		// add the BaseType to the list
                this->BaseTypes.push_back(Id);
                
		// rebuild the Graph
		// !TODO only mark the graph for rebuilding and rebuild it only, when needed.
		// 	 or implement extension
		this->rebuildGraph();
		return 0;
        }
        return -1;
}

/**
* Register a new Conversion
* @param Id the Id of the Conversion to register
* @return 0 on success, -1 on failure
*/
int ConversionManager::registerConversion(ConversionId* Id)
{
	bool alreadyInList = false;
	foreach(ConversionId* registeredConversion, Conversions) {
		if( registeredConversion->getConversionName().compare(Id->getConversionName()) == 0){
#if DEBUG
		cout << Id->getConversionName() << " has already been registered!\n";
#endif
		alreadyInList = true;
		}
	}
	if(!alreadyInList){
		// add the Conversion to the list
		
		// Extend the existing graph
		int from = this->getVertexNumberbyName(Id->getSourceId()->getTypeName());
		int to	 = this->getVertexNumberbyName(Id->getTargetId()->getTypeName());
		if(from != -1 && to != -1)
		{
			this->Conversions.push_back(Id);
			
			this->CGraph->addEdge(E(from,to),Id->getEdgeWeight());
			return 0;
		}
	}
	return -1;
}

string ConversionManager::strGetBaseTypeNames()
{
        string Names = "{ ";
        foreach(BaseTypeId* registeredBaseType, BaseTypes) {
                Names += registeredBaseType->getTypeName() + " ";
        }
        Names += "}";
	
        return Names;
}

string ConversionManager::strGetConversionNames()
{
	string Names = "{ ";
        foreach(ConversionId* registeredConversion, Conversions) {
                Names +=  registeredConversion->getConversionName() + " ";
        }
        Names += "}";
        return Names;
}

/**
 * Get the route from SourceType to TargetType
 * @return ConversionRouting
 * @param  SourceType
 * @param  TargetType
 */
ConversionRoute ConversionManager::route (BaseType SourceType, BaseType TargetType )
{
	ConversionRoute RT;
	return RT;
}

ConversionId* ConversionManager::getConversion(int VertexFrom, int VertexTo)
{
	foreach(ConversionId* Candidate, this->Conversions)
  	{
		if(getVertexNumberbyName(Candidate->getSourceId()->getTypeName()) == VertexFrom &&
		  getVertexNumberbyName(Candidate->getTargetId()->getTypeName()) == VertexTo )
		{
			return Candidate;
		}

	}
	return NULL;
}

ConversionRoute ConversionManager::route (std::vector<BaseTypeId*> SourceType, std::vector<BaseTypeId*> TargetType )
{
	ConversionRoute CR = ConversionRoute(); 
	foreach(BaseTypeId* SourceBaseType, SourceType)
	{
	#if DEBUG
		cout << "Findinng the shortest Path from " << SourceBaseType->getTypeName()  << endl;
	#endif
		ShortestPaths paths =  getShortestPaths(this->getVertexNumberbyName(SourceBaseType->getTypeName()));
		int min = 100; // (int)(std::numeric_limits < short >::max)()-1;
		int minVertexNumber = -1;
		BaseTypeId* minId = NULL;
		
		// Find the shortest Distance
		
		
		
		foreach(BaseTypeId* TargetBaseType, TargetType)
		{
			
			
			int testindex = this->getVertexNumberbyName(TargetBaseType->getTypeName());
			if(min >(*(paths.distances))[testindex] )
			{
				min = (*(paths.distances))[testindex];
				minId = TargetBaseType;
				#if DEBUG
				cout << "TargetBaseType found: " << TargetBaseType->getTypeName() << endl;
				#endif 
			}	
		}
		
		cout << "minVertexNumber: " << minVertexNumber << endl; 
		#if DEBUG
		if(minId != NULL)
			cout << "The closest TargetType is " << minId->getTypeName() << endl;	
		else 
			cout << "Conversion not possible" << endl;
		#endif
	

		if(minId != NULL){
		// add the Route to the ConversionRoute
		ConversionPath* newPath = new ConversionPath();
		newPath->setBaseTypeFrom(SourceBaseType);
		newPath->setBaseTypeTo(minId);
		int index = this->getVertexNumberbyName(minId->getTypeName());
		while(index != this->getVertexNumberbyName(SourceBaseType->getTypeName()) )
		{
			int pv_index = (*(paths.parents))[index];
			newPath->addConversion(this->getConversion(pv_index,index));
			index = pv_index;
		}
		CR.addConversionPath(newPath);
		}
	
	}

	return CR;
}
#endif
